#ifndef SCENENODE_H
#define SCENENODE_H

#include "bga/system/Tree.h"
#include "bga/system/DepthFirstTreeIterator.h"

#include "bga/graphics/Drawable.h"
#include "bga/graphics/Uniform.h"
#include "bga/graphics/Program.h"
#include "bga/graphics/Texture2D.h"
#include "bga/graphics/FrameBufferObject.h"
#include "bga/graphics/SceneNodeListener.h"

#include "bga/math/Matrix.h"

#include <vector>

/**
 * \namespace bga
 * \brief API namespace
 */
namespace bga
{

class SceneNode;

struct SceneNodeElement
{
    SceneNodeElement() : owner(NULL), drawable(NULL), program(NULL), currentProgram(NULL),
    fbo(NULL), alternativeFbo(NULL), currentFbo(NULL), alpha(1.0)
    {
    }

    SceneNode* owner;
    Drawable* drawable;
    /// TODO : replace both arrays with matrix
    Matrix4f transformations;   /// transformation configured for this node
    Matrix4f currentTransformation; /// transformation apply at this node
    /**float transformations[16];
    float currentTransformation[16]; */
    Program* program;
    Program* currentProgram;
    FrameBufferObject* fbo; /// used to select a other render target
    FrameBufferObject* alternativeFbo;
    FrameBufferObject* currentFbo;
    std::vector<Uniform*> uniformList; /// uniform list configured for this node
    std::vector<Uniform*> currentUniformList; /// uniform list apply at this node
    std::vector<TextureObject*> texture;
    std::vector<TextureObject*> currentTexture;
    float alpha; /// transparency factor [0-1]
};

typedef Tree<SceneNodeElement> SceneGraph;
typedef DepthFirstTreeIterator<SceneNodeElement> SceneGraphIterator;

/*!
 * \class SceneNode
 * \brief
 *
 *
 */
class BOARD_HELL_ENGINE SceneNode : public Node<SceneNodeElement>
{

	public:
		SceneNode();
		virtual ~SceneNode();

        /*!
         * \brief add Drawable to SceneNode
         */
		void addDrawable(Drawable& drawable);

        /*!
         * \brief add Texture2D to SceneNode
         */
        void addTexture(unsigned int unit, TextureObject& texture);

        /*!
         * \brief set Program shader used for this node
         */
        void setProgram(Program& program);

        /*!
         * \brief set a FrameBufferObject to select an other render target for this node
         */
        void setFrameBufferObject(FrameBufferObject& fbo);
        void setAlternativeFrameBufferObject(FrameBufferObject& fbo);

        /*!
         * \brief add Uniform to this node
         */
		void addUniform(Uniform& uniform);

        /*!
         * \brief get Uniform
         */
		Uniform* getUniform(std::string name) const;

        /*!
         * \brief set alpha factor for this node to ajust transparency
         */
		void setAlpha(float alpha);
        float getAlpha() const;

        /*!
         * \brief indicate if this node must be process at the end (for example offscreen renderer)
         */
		void setPostProcess(bool postProcess);
        bool getPostProcess() const;

        void translate(float x, float y, float z);
        void scale(float x, float y, float z);
        void rotateX(double theta);
        void rotateY(double theta);
        void rotateZ(double theta);

        /*!
         * \brief update this SceneNode
         */
        void update();

        /*!
         * \brief process this SceneNode
         */
        void process();

        /*!
         * \brief Indicate if this SceneNode inherit transformation from his parent
         */
        void setInheritParentTransformation(bool inherit);
        bool getInheritParentTransformation() const;

        /*!
         * \brief Indicate if this node reflect his direct children according to z direction.
         * In this case relfexion intensity is function of children alpha
         */
        void setReflect(bool reflect);
        bool getReflect() const;

	protected:

        /// boolean to indicate if elements need to be configured
        bool dirtyDrawable;
        std::vector<bool> dirtyTexture;
        bool dirtyProgram;
        bool dirtyFbo;
        bool dirtyAlternativeFbo;

        bool inheritParentTransformation;
        bool reflect;
        bool postProcess;

        SceneNodeListener listener; ///!< Listener allowing to update graphic elements(VBO configuration, ...)

	private:

};


}

#endif // SCENENODE_H

